package ide.ccf.ccproject;

import ide.ccf.ccproject.common.Utils;
import ide.ccf.ccproject.launch.LaunchConfig;
import ide.ccf.ccproject.preferences.CCFPreferencePage;

import java.io.File;
import java.io.FileFilter;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.ICDescriptor;
import org.eclipse.cdt.core.ICDescriptorOperation;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.IIncludeEntry;
import org.eclipse.cdt.core.model.IPathEntry;
import org.eclipse.cdt.core.resources.IPathEntryStore;
import org.eclipse.cdt.core.settings.model.ICProjectDescription;
import org.eclipse.cdt.core.settings.model.ICProjectDescriptionManager;
import org.eclipse.cdt.make.core.IMakeBuilderInfo;
import org.eclipse.cdt.make.core.IMakeTarget;
import org.eclipse.cdt.make.core.IMakeTargetManager;
import org.eclipse.cdt.make.core.MakeBuilder;
import org.eclipse.cdt.make.core.MakeCorePlugin;
import org.eclipse.cdt.make.core.MakeProjectNature;
import org.eclipse.cdt.make.core.scannerconfig.IScannerConfigBuilderInfo2;
import org.eclipse.cdt.make.core.scannerconfig.ScannerConfigNature;
import org.eclipse.cdt.make.internal.core.scannerconfig2.ScannerConfigProfileManager;
import org.eclipse.cdt.managedbuilder.core.IBuilder;
import org.eclipse.cdt.managedbuilder.core.IConfiguration;
import org.eclipse.cdt.managedbuilder.core.IManagedBuildInfo;
import org.eclipse.cdt.managedbuilder.core.IManagedProject;
import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager;
import org.eclipse.cdt.managedbuilder.core.ManagedBuilderCorePlugin;
import org.eclipse.cdt.managedbuilder.internal.core.ManagedBuildInfo;
import org.eclipse.cdt.managedbuilder.internal.core.ManagedProject;
//import org.eclipse.cdt.newmake.core.IMakeBuilderInfo;
import org.eclipse.cdt.newmake.core.IMakeCommonBuildInfo;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;


/**
 * CCF项目类
 *
 */
@SuppressWarnings({ "deprecation", "restriction" })
public class CCFProject 
{
	private static final String VERSION = "ide.ccf.ccproject.properties.version";
 	private static final String PROJECTFILECHANGELISTENER = "ide.ccf.ccproject.properties.projectfilechangelistener";
	private static final String RELEASE_NAME = "CCF Release Build";
	private static final String DEBUG_NAME = "CCF Debug Build";
	private static final String[] INCLUDE_PATHS = {"", "ActiveQt", "phonon", "Qt3Support", "QtAssistant", "QtCore", "QtDBus", "QtDesigner", "QtGui",
		"QtHelp", "QtNetwork", "QtOpenGL", "QtScript", "QtSql", "QtSvg", "QtTest", "QtUiTools", "QtWebKit", "QtXml", "QtXmlPatterns"};
	private IProject wrapped;
	
	/**
	 * 构造器
	 * @param wrappedProject
	 */
	public CCFProject(IProject wrappedProject) 
	{
		wrapped = wrappedProject;
	}
	
	/**
	 * 获取项目
	 * @return
	 */
	public IProject getProject() 
	{
		return wrapped;
	}
	
	/**
	 * 获取Bin路径
	 * @return
	 */
	public String getBinPath() //todo mencius
	{
		try 
		{
			String version = wrapped.getPersistentProperty(new QualifiedName("", VERSION));
			return CCFPreferencePage.getCCFVersionBinPath(version);
		} 
		catch (CoreException e) 
		{
			e.printStackTrace();
			return null;
		}
	}
	
	/**
	 * 获取Include路径
	 * @return
	 */
	public String getIncludePath()  //todo mencius
	{
		try 
		{
			String version = wrapped.getPersistentProperty(new QualifiedName("", VERSION));
			return CCFPreferencePage.getCCFVersionIncludePath(version);
		} 
		catch (CoreException e) 
		{
			e.printStackTrace();
			return null;
		}
		//return null;
	}

	/**
	 * 获取CCF版本
	 * @return
	 */
	public String getVersion() 
	{
		try 
		{
			return wrapped.getPersistentProperty(new QualifiedName("", VERSION));
		} 
		catch (CoreException ex) 
		{
			ex.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 设置CCF版本
	 * @param version
	 * @return
	 */
	public boolean setVersion(String version) 
	{
		try 
		{
			String oldBinPath = getBinPath();
			String oldIncludePath = getIncludePath();
			wrapped.setPersistentProperty(new QualifiedName("", VERSION), version);
			updateDir(oldBinPath, oldIncludePath);
		} 
		catch (CoreException e) 
		{
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	/**
	 * 项目文件变化时自动运行qmake
	 * @return
	 */
	public boolean runQMakeWhenProjectFileChanges() 
	{
		try 
		{
			String value = wrapped.getPersistentProperty(new QualifiedName("", PROJECTFILECHANGELISTENER));
			return (value != null && value.equals("true"));
		} 
		catch (CoreException ex) 
		{
			ex.printStackTrace();
		}
		return false;
	}
	
	/**
	 * 设置自动运行qmake
	 * @param enable
	 */
	public void setRunQMakeWhenProjectFileChanges(boolean enable) 
	{
		try 
		{
			wrapped.setPersistentProperty(new QualifiedName("", PROJECTFILECHANGELISTENER), enable ? "true" : "false");
		} 
		catch (CoreException e) 
		{
			e.printStackTrace();
		}
	}

	/**
	 * 更新CCF版本
	 * @param oldBinPath
	 * @param oldIncludePath
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public void updateDir(String oldBinPath, String oldIncludePath) 
	{
		try 
		{
			if (!wrapped.hasNature(CCFNature.NATURE_ID))
				return;
			
/*			if (!wrapped.hasNature(MakeProjectNature.NATURE_ID))
				return;*/

			String binPath = getBinPath();
			String includePath = getIncludePath();
			
			if (binPath == null || includePath == null)
				return;
			
			String mkspec = Utils.getMakeSpec(new Path(binPath).removeLastSegments(1).toOSString());
			boolean setmkcmd = CCFPreferencePage.getAutoSetMkCmd(); 
			String mkcmd = null;
			
			if (setmkcmd)
				mkcmd = Utils.getMakeCommand(mkspec);
		
			
			/*IMakeBuilderInfo info = MakeCorePlugin.createBuildInfo(wrapped, MakeBuilder.BUILDER_ID);
			Map env = info.getEnvironment();
			
			if (CCFPreferencePage.getAutoSetMkSpec() && mkspec != null)
				env.put("QMAKESPEC", mkspec);

			String path = Utils.createPath((String)env.get("PATH"), binPath, oldBinPath);
			env.put("PATH", path);
			info.setEnvironment(env);
			
			if (setmkcmd) 
			{
				info.setBuildAttribute(IMakeBuilderInfo.BUILD_COMMAND, mkcmd);
				
				// 设置debug编译为默认
				info.setBuildAttribute(IMakeBuilderInfo.BUILD_TARGET_AUTO, "debug");
				info.setBuildAttribute(IMakeBuilderInfo.BUILD_TARGET_INCREMENTAL, "debug");
			}
			
			IMakeTargetManager targetManager = MakeCorePlugin.getDefault().getTargetManager();
			String[] builders = targetManager.getTargetBuilders(wrapped);
			
			for (int i = 0; i < builders.length; ++i) 
			{
				IMakeTarget target = targetManager.findTarget(wrapped, RELEASE_NAME);
				
				if (target != null) 
				{
					if (setmkcmd)
						target.setBuildAttribute(IMakeTarget.BUILD_COMMAND, mkcmd);
					
					target.setEnvironment(env);					
				}
				
				target = targetManager.findTarget(wrapped, DEBUG_NAME);
				
				if (target != null) 
				{
					if (setmkcmd)
						target.setBuildAttribute(IMakeTarget.BUILD_COMMAND, mkcmd);											
					target.setEnvironment(env);
				}
			}	*/
			
			LaunchConfig.updateLaunchPaths(wrapped, binPath, oldBinPath); // 更新启动设置
			//@！mencius注释掉下句。
			//@！注释的问题是这一句会将ccf的未裁剪的include路径写到工程include目录中，导致编译过程产生冲突，无法编译成功。
			//@！时间：20161214
			//updateIncludeDir(includePath, oldIncludePath); // 更新Include路径
		} 
		catch (CoreException e) 
		{
			// 如果.project文件为只读，则跑出异常
			e.printStackTrace();
		}
	}

	/**
	 * 重新构建
	 */
	public void scheduleRebuild() 
	{
		final IProject project = wrapped;
		String name = project.getName();
		WorkspaceJob cleanJob = new WorkspaceJob("清除" + project.getName()) 
		{
    		public boolean belongsTo(Object family) 
    		{
    			return ResourcesPlugin.FAMILY_MANUAL_BUILD.equals(family);
    		}
    		
			public IStatus runInWorkspace(IProgressMonitor monitor) 
			{
				try 
				{
					project.build(IncrementalProjectBuilder.CLEAN_BUILD, monitor);
					WorkspaceJob buildJob = new WorkspaceJob("构建" + project.getName()) 
					{
			    		public boolean belongsTo(Object family) 
			    		{
			    			return ResourcesPlugin.FAMILY_MANUAL_BUILD.equals(family);
			    		}
			    		
						public IStatus runInWorkspace(IProgressMonitor monitor) 
						{
							try 
							{
								project.build(IncrementalProjectBuilder.INCREMENTAL_BUILD, monitor);
							} 
							catch (CoreException e) 
							{
							}
							return Status.OK_STATUS;
						}
					};
					buildJob.setRule(project.getWorkspace().getRuleFactory().buildRule());
					buildJob.setUser(true);
					buildJob.schedule();
				} 
				catch (CoreException e) 
				{
				}
                return Status.OK_STATUS;
    		}
    	};
        cleanJob.setRule(project.getWorkspace().getRuleFactory().buildRule());
        cleanJob.setUser(true);
        cleanJob.schedule();
	}	

	/**
	 * 转换为CCF项目
	 * @param monitor
	 * @throws CoreException
	 */
	@SuppressWarnings({ "deprecation", "restriction" })
	public void convertToCCFProject(IProgressMonitor monitor) throws CoreException
	{
		if (!wrapped.hasNature(org.eclipse.cdt.make.core.MakeProjectNature.NATURE_ID))
			org.eclipse.cdt.core.CCorePlugin.getDefault().convertProjectToCC(wrapped, monitor, MakeCorePlugin.MAKE_PROJECT_ID);
		
		if (!wrapped.hasNature(MakeProjectNature.NATURE_ID))
		{
			System.out.println("-----------------------no MakeProjectNature ------------------------------");
			MakeProjectNature.addNature(wrapped, new SubProgressMonitor(monitor, 1));
		}
		else
			System.out.println("-----------------------have MakeProjectNature ------------------------------");
				
		
		if (!wrapped.hasNature(ScannerConfigNature.NATURE_ID)) 
		{
			ScannerConfigNature.addScannerConfigNature(wrapped);
			IScannerConfigBuilderInfo2 scannerConfig = ScannerConfigProfileManager.createScannerConfigBuildInfo2(wrapped, "org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile");
			scannerConfig.save();
		}
		addNature(monitor);
	}	

	/**
	 * 添加CCF项目性质
	 * @param monitor
	 * @throws CoreException
	 */
	private void addNature(IProgressMonitor monitor) throws CoreException
	{
		IProjectDescription description = wrapped.getDescription();
		String[] natures = description.getNatureIds();
		String[] newNatures = new String[natures.length + 1];
		
		System.arraycopy(natures, 0, newNatures, 0, natures.length);
		newNatures[natures.length + 0] = ProjectConstants.NATURE_ID;
		description.setNatureIds(newNatures);
		wrapped.setDescription(description, monitor);
		
		//configureMake();
	}

	/**
	 * 更新Include路径
	 * @param newIncludePath
	 * @param oldIncludePath
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private void updateIncludeDir(String newIncludePath, String oldIncludePath)
	{
		try 
		{
			Vector v = new Vector();
			IPathEntryStore store = CoreModel.getPathEntryStore(wrapped);
			if(newIncludePath.equals("/opt/ccf/runtime/ccf.gnome.sdk/x86_64/1.0/files/include"))
			{
				String[] includePaths = {"/usr/include/c++/5.3.0", "/usr/include/c++/5.3.0/x86_64-unknown-linux", 
						"/usr/include/c++/5.3.0/backward", "/usr/lib/gcc/x86_64-unknown-linux/5.3.0/include",
						"/usr/lib/gcc/x86_64-unknown-linux/5.3.0/include-fixed"};
			
				//change by mencius 
				for(int j = 0; j < includePaths.length; j++)
				{
					IIncludeEntry pathEntry = CoreModel.newIncludeEntry(new Path(""), new Path(includePaths[j]), new Path(""));
					v.add(pathEntry);
				}

				// 删除旧的路径入口并且复制路径
				IPathEntry[] paths = store.getRawPathEntries();
				
				for (int i = 0; i < paths.length; ++i) 
				{
					if (paths[i] instanceof IIncludeEntry) 
					{
						String oldIndexerIncludePath = ((IIncludeEntry)paths[i]).getFullIncludePath().toOSString();
						
						if ((oldIncludePath != null && oldIndexerIncludePath.startsWith(oldIncludePath)) || oldIndexerIncludePath.startsWith(newIncludePath))
							continue;
					}
					v.add(paths[i]);
				}
			}
			else
			{
				String[] includePaths;
				File newDirectory = new File(newIncludePath);
				
				if (newDirectory.canRead() && newDirectory.isDirectory()) 
				{
					File[] subDirectories = newDirectory.listFiles(new FileFilter()
					{
						public boolean accept(File pathname) 
						{
							if (pathname.canRead() && pathname.isDirectory())
								return true;
							
							return false;
						}
					});
					
					includePaths = new String[subDirectories.length+1];
					includePaths[0] = "";
					
					for (int i = 0; i < subDirectories.length; ++i)
						includePaths[i+1] = subDirectories[i].getName();	
				} 
				else 
				{
					includePaths = INCLUDE_PATHS;
				}
				
				for (int i = 0; i < includePaths.length; i++) 
				{
					IIncludeEntry pathEntry = CoreModel.newIncludeEntry(new Path(""), new Path(newIncludePath), new Path(includePaths[i]));
					v.add(pathEntry);
				}

				// 删除旧的路径入口并且复制路径
				IPathEntry[] paths = store.getRawPathEntries();
				
				for (int i=0; i<paths.length; ++i) 
				{
					if (paths[i] instanceof IIncludeEntry) 
					{
						String oldIndexerIncludePath = ((IIncludeEntry)paths[i]).getFullIncludePath().toOSString();
						
						if ((oldIncludePath != null && oldIndexerIncludePath.startsWith(oldIncludePath)) || oldIndexerIncludePath.startsWith(newIncludePath))
							continue;
					}
					v.add(paths[i]);
				}
			}
			
			store.setRawPathEntries((IPathEntry[])v.toArray(new IPathEntry[v.size()]));
		} 
		catch (CoreException e) 
		{
			
		}
	}
	
	/**
	 * 更新Include路径
	 * @param newIncludePath
	 * @param oldIncludePath
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private void updateIncludeDir2(String newIncludePath, String oldIncludePath)
	{
		try 
		{
			Vector v = new Vector();
			IPathEntryStore store = CoreModel.getPathEntryStore(wrapped);
			String[] includePaths;
			File newDirectory = new File(newIncludePath);
			
			if (newDirectory.canRead() && newDirectory.isDirectory()) 
			{
				File[] subDirectories = newDirectory.listFiles(new FileFilter()
				{
					public boolean accept(File pathname) 
					{
						if (pathname.canRead() && pathname.isDirectory())
							return true;
						
						return false;
					}
				});
				
				includePaths = new String[subDirectories.length+1];
				includePaths[0] = "";
				
				for (int i = 0; i < subDirectories.length; ++i)
					includePaths[i+1] = subDirectories[i].getName();	
			} 
			else 
			{
				includePaths = INCLUDE_PATHS;
			}
			
			for (int i = 0; i < includePaths.length; i++) 
			{
				IIncludeEntry pathEntry = CoreModel.newIncludeEntry(new Path(""), new Path(newIncludePath), new Path(includePaths[i]));
				v.add(pathEntry);
			}

			// 删除旧的路径入口并且复制路径
			IPathEntry[] paths = store.getRawPathEntries();
			
			for (int i=0; i<paths.length; ++i) 
			{
				if (paths[i] instanceof IIncludeEntry) 
				{
					String oldIndexerIncludePath = ((IIncludeEntry)paths[i]).getFullIncludePath().toOSString();
					
					if ((oldIncludePath != null && oldIndexerIncludePath.startsWith(oldIncludePath)) || oldIndexerIncludePath.startsWith(newIncludePath))
						continue;
				}
				v.add(paths[i]);
			}
			store.setRawPathEntries((IPathEntry[])v.toArray(new IPathEntry[v.size()]));
		} 
		catch (CoreException e) 
		{
			
		}
	}
	

	/**
	 * 配置make
	 */
	private void configureMake() 
	{
		try 
		{
			IMakeBuilderInfo info = MakeCorePlugin.createBuildInfo(wrapped, MakeBuilder.BUILDER_ID);
			info.setAppendEnvironment(true);
			info.setUseDefaultBuildCmd(false);
			info.setBuildAttribute(IMakeBuilderInfo.BUILD_TARGET_AUTO, "");
			info.setBuildAttribute(IMakeBuilderInfo.BUILD_TARGET_INCREMENTAL, "");
		} 
		catch (CoreException e) 
		{
			e.printStackTrace();
		}
		
		if (Platform.getOS().equals(Platform.OS_WIN32))
			setBinaryParser("org.eclipse.cdt.core.PE");

		createTargets();
		updateDir("", "");
	}

	/**
	 * 设置二进制文件解析器
	 * @param id
	 */
	@SuppressWarnings("deprecation")
	private void setBinaryParser(String id) 
	{
		class TmpICDescriptorOperation implements ICDescriptorOperation 
		{
			public String m_id;
			public void execute(ICDescriptor descriptor, IProgressMonitor monitor) throws CoreException 
			{
				descriptor.create(CCorePlugin.BINARY_PARSER_UNIQ_ID, m_id);
			}			
		}
		
		TmpICDescriptorOperation op = new TmpICDescriptorOperation();
		op.m_id = id;
		
		try 
		{
			CCorePlugin.getDefault().getCDescriptorManager().runDescriptorOperation(wrapped, op, null);
		} 
		catch (CoreException e) 
		{
			e.printStackTrace();
		}
	}

	/**
	 * 创建项目构建目标
	 */
	private void createTargets() 
	{
		MakeCorePlugin makecore = MakeCorePlugin.getDefault();
		IMakeTargetManager targetManager = makecore.getTargetManager();
		
		try 
		{
			String[] builders = targetManager.getTargetBuilders(wrapped);
			
			for (int i = 0; i < builders.length; ++i) 
			{
				IMakeTarget target = targetManager.findTarget(wrapped, RELEASE_NAME);
				
				if (target == null) 
				{
					target = targetManager.createTarget(wrapped, RELEASE_NAME, builders[i]);
					targetManager.addTarget(target);
				}
				
				initTarget(target, "release");
				
				target = targetManager.findTarget(wrapped, DEBUG_NAME);
				if (target == null) 
				{
					target = targetManager.createTarget(wrapped, DEBUG_NAME, builders[i]);
					targetManager.addTarget(target);
				}
				
				initTarget(target, "debug");
			}
		} 
		catch (CoreException e) 
		{
			e.printStackTrace();
		}
	}
	
	/**
	 * 初始化目标
	 * @param target
	 * @param config
	 */
	private static void initTarget(IMakeTarget target, String config) 
	{
		try 
		{
			target.setBuildAttribute(IMakeTarget.BUILD_TARGET, config);
			target.setUseDefaultBuildCmd(false);
		} 
		catch (CoreException e) 
		{
			e.printStackTrace();
		}
	}
}